package 数组算法;


import java.util.*;
import java.util.stream.Collectors;

public class topKFrequent347 {

    public int[] topKFrequent(int[] nums, int k) {
        HashMap<String, Integer> map = new HashMap<>();

        for (int i : nums) {
            String num = String.valueOf(i);
            if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                map.put(num, map.get(num) + 1);
            }
        }

        Map<String, Integer> result = map.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));

        int[] ans = new int[k];
        int index = 0;
        for (String s : result.keySet()) {
            if (k > 0) {
                ans[index++] = Integer.valueOf(s);
                k--;
            } else {
                break;
            }
        }
        Arrays.sort(ans);
        return ans;
    }

    public int[] topKFrequentV2(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();

        for (int i : nums) {
            if (!map.containsKey(i)) {
                map.put(i, 1);
            } else {
                map.put(i, map.get(i) + 1);
            }
        }

        ArrayList<int[]> list = new ArrayList<>();

        for (int i : map.keySet()) {
            list.add(new int[]{i, map.get(i)});
        }

        Collections.sort(list, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[1] - o1[1];
            }
        });

        int[] ans = new int[k];
        int index = 0;

        for (int[] arr : list) {
            if (k > 0) {
                ans[index++] = arr[0];
                k--;
            } else {
                break;
            }
        }
        return ans;
    }

    /**
     * 在这里，我们可以利用堆的思想：建立一个小顶堆，然后遍历「出现次数数组」：
     * 如果堆的元素个数小于 kk，就可以直接插入堆中。
     * 如果堆的元素个数等于 kk，则检查堆顶与当前出现次数的大小。
     * 如果堆顶更大，说明至少有 k个数字的出现次数比当前值大，故舍弃当前值；
     * 否则，就弹出堆顶，并将当前值插入堆中。
     *
     * @param nums
     * @param k
     * @return
     */
    public int[] topKFrequentV3(int[] nums, int k) {
        Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
        for (int num : nums) {
            occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
        }

        // int[] 的第一个元素代表数组的值，第二个元素代表了该值出现的次数
        PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
            @Override
            public int compare(int[] m, int[] n) {
                return m[1] - n[1];
            }
        });
        for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
            int num = entry.getKey(), count = entry.getValue();
            if (queue.size() == k) {
                if (queue.peek()[1] < count) {
                    queue.poll();
                    queue.offer(new int[]{num, count});
                }
            } else {
                queue.offer(new int[]{num, count});
            }
        }
        int[] ret = new int[k];
        for (int i = 0; i < k; ++i) {
            ret[i] = queue.poll()[0];
        }
        return ret;
    }
}